/*
 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace ark::intrinsics {

// Autogenerated file -- DO NOT EDIT!
% Runtime::intrinsics.select(&:has_impl?).uniq { |intrn| intrn.impl + ", " + intrn.impl_signature.args.count.to_s + ";;" + intrn.private.to_s}.each do |intrinsic|
%   next if intrinsic.is_stub
%   params = intrinsic.impl_signature.args.each_with_index.map {|cpp_type, index| cpp_type + " " + "arg#{index}" }
%   params = params.unshift("[[maybe_unused]] Method* /* unused */")
%   implementation = intrinsic.impl.rpartition("::").last
%     if intrinsic.private
#ifndef PANDA_PRODUCT_BUILD
%     end
extern "C" <%= intrinsic.impl_signature.ret %> <%= implementation %>BridgeSelectorEntryPoint(<%= params.join(", ") %>);
// NOLINTNEXTLINE(misc-definitions-in-headers)
extern "C" <%= intrinsic.impl_signature.ret %> <%= implementation %>CompiledAbi(<%= params.join(", ") %>) {  // NOLINT
%   nargs = intrinsic.impl_signature.args.size
%   arg_list = (0...nargs).map { |i| "arg#{i}" }.join(", ")
%   ret_type = intrinsic.impl_signature.ret
%   if ret_type == 'void'
    <%= intrinsic.impl %>(<%= arg_list %>);
%   else
    return <%= intrinsic.impl %>(<%= arg_list %>);
% end
}
%     if intrinsic.private
#endif  // PANDA_PRODUCT_BUILD
%     end

% end

// NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers)
bool Initialize(ark::panda_file::SourceLang vmLang) {
    Runtime *runtime = Runtime::GetCurrent();
    ClassLinker *classLinker = runtime->GetClassLinker();
    const auto& runtimeOptions = Runtime::GetOptions();

    if (!runtimeOptions.ShouldInitializeIntrinsics()) {
        return true;
    }

    if (!classLinker->HasExtension(vmLang)) {
        // No extension here.
        return true;
    }

    ClassLinkerExtension *cle = classLinker->GetExtension(vmLang);

    auto spaces = runtimeOptions.GetBootIntrinsicSpaces();

#ifndef PANDA_PRODUCT_BUILD
    auto blacklist = runtimeOptions.GetIntrinsicsBlacklist();
#endif // PANDA_PRODUCT_BUILD

    std::string_view space;
    for (const auto &spaceArg : spaces) {
        if (runtime->GetLanguageContext(spaceArg).GetLanguage() == vmLang) {
            space = spaceArg;
            break;
        }
    }

    if (space.empty()) {
        // Current space was disabled in options.
        return true;
    }

% Runtime::intrinsics.select{ |i| !i.signature.stackrange && !i.is_stub && !i.compiler_only }.select(&:has_impl?).each do |intrinsic|
%     if intrinsic.private
#ifndef PANDA_PRODUCT_BUILD
%     end
    // Init intrinsics only for current vm.
    if (space == "<%= intrinsic.space %>") {
        auto mutf8Name = reinterpret_cast<const uint8_t *>("<%= get_object_descriptor(intrinsic.class_name) %>");
        auto klass = cle->GetClass(mutf8Name);
        if (klass == nullptr) {
            LOG(ERROR, RUNTIME) << "Cannot find class '" << mutf8Name << "'";
            return false;
        }
        mutf8Name = reinterpret_cast<const uint8_t *>("<%= intrinsic.method_name %>");

        Method::Proto proto;
        auto &shorty = proto.GetShorty();

%   types = [intrinsic.signature.ret] + intrinsic.signature.args
%   types.each do |t|
        shorty.emplace_back(panda_file::Type::TypeId::<%= get_shorty_type(t) %>);
%     if object_type?(t)
        proto.GetRefTypes().emplace_back("<%= get_object_descriptor(t) %>");
%     end
%   end

        auto method = klass->GetDirectMethod(mutf8Name, proto);
        if (method == nullptr) {
            LOG(ERROR, RUNTIME) << "Cannot find method '<%= intrinsic.class_name %>.<%= intrinsic.method_name %>' in space '<%= intrinsic.space %>'";
            return false;
        }

%       method_full_name = intrinsic.class_name + "::" + intrinsic.method_name
#ifndef PANDA_PRODUCT_BUILD
        if (!blacklist.empty() && std::find(blacklist.begin(), blacklist.end(), static_cast<const char *>("<%= method_full_name %>")) != blacklist.end()) {
           LOG(DEBUG, RUNTIME) << "Skipping intrinsic linkage for blacklisted method <%= method_full_name %>";
        } else {
#endif // PANDA_PRODUCT_BUILD
            method->SetIntrinsic(Intrinsic::<%= intrinsic.enum_name %>);
            method->SetCompiledEntryPoint(reinterpret_cast<const void *>(<%= intrinsic.impl.rpartition("::").last + "BridgeSelectorEntryPoint" %>));
#ifndef PANDA_PRODUCT_BUILD
        }
#endif // PANDA_PRODUCT_BUILD
    }
%     if intrinsic.private
#endif  // PANDA_PRODUCT_BUILD
%     end
% end
    return true;
}

}  // namespace ark::intrinsics

% Runtime::intrinsics.select(&:has_impl?).uniq { |i| i.impl }.each do |intrinsic|
%   next if !intrinsic.need_abi_wrapper?
%   namespace, _, funcname = intrinsic.impl.rpartition('::')
namespace <%= namespace %> {
%   params = intrinsic.impl_signature.args.each_with_index.map {|cpp_type, index| cpp_type + " " + "arg#{index}" }
<%= intrinsic.impl_signature.ret %> <%= funcname %>(<%= params.join(", ") %>) {  // NOLINT
%   nargs = intrinsic.impl_signature.args.size
%   arg_list = (0...nargs).map do |i|
%     if intrinsic.impl_signature.args[i] == intrinsic.orig_impl_signature.args[i]
%       "arg#{i}"
%     else
%       "static_cast<#{intrinsic.orig_impl_signature.args[i]}>(arg#{i})"
%     end
%   end
%   arg_list = arg_list.join(", ")
%   ret_type = intrinsic.orig_impl_signature.ret
%   if ret_type == 'void'
    <%= intrinsic.orig_impl %>(<%= arg_list %>);
%   else
    return <%= intrinsic.orig_impl %>(<%= arg_list %>);
%   end
}
}  // namespace <%= namespace %>

% end
